/*
 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package top.continew.admin.auth.handler;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.AbstractLoginHandler;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.WechatMiniProgramLoginReq;
import top.continew.admin.auth.model.resp.WechatUserInfo;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.auth.service.WechatApiService;
import top.continew.admin.common.constant.RegexConstants;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.common.enums.GenderEnum;
import top.continew.admin.common.enums.RoleCodeEnum;
import top.continew.admin.system.constant.SystemConstants;
import top.continew.admin.system.enums.MessageTemplateEnum;
import top.continew.admin.system.enums.MessageTypeEnum;
import top.continew.admin.system.model.entity.user.UserDO;
import top.continew.admin.system.model.entity.user.UserSocialDO;
import top.continew.admin.system.model.req.MessageReq;
import top.continew.admin.system.model.resp.ClientResp;
import top.continew.admin.system.service.MessageService;
import top.continew.admin.system.service.UserRoleService;
import top.continew.admin.system.service.UserSocialService;
import top.continew.starter.core.autoconfigure.application.ApplicationProperties;
import top.continew.starter.core.exception.BadRequestException;

import java.time.LocalDateTime;
import java.util.Collections;

/**
 * 微信小程序登录处理器
 *
 * @author Charles7c
 * @since 2024/12/22
 */
@Component
@RequiredArgsConstructor
public class WechatMiniProgramLoginHandler extends AbstractLoginHandler<WechatMiniProgramLoginReq> {

    private final WechatApiService wechatApiService;
    private final UserSocialService userSocialService;
    private final UserRoleService userRoleService;
    private final MessageService messageService;
    private final ApplicationProperties applicationProperties;

    @Override
    public LoginResp login(WechatMiniProgramLoginReq req, ClientResp client, HttpServletRequest request) {
        // 获取微信用户信息
        WechatUserInfo wechatUserInfo;

        try {
            wechatUserInfo = wechatApiService.getUserInfo(req.getCode());
        } catch (BadRequestException e) {
            throw new BadRequestException("获取微信用户信息失败：" + e.getMessage());
        }
        
        // 查询是否已绑定本地用户
        String source = "WECHAT_MINI_PROGRAM";
        String openId = wechatUserInfo.getOpenid();
        UserSocialDO userSocial = userSocialService.getBySourceAndOpenId(source, openId);
        
        UserDO user;
        if (userSocial == null) {
            // 首次登录，自动注册新用户
            user = this.registerNewUser(wechatUserInfo);
            
            // 分配默认角色
            userRoleService.assignRolesToUser(Collections.singletonList(roleService
                .getIdByCode(RoleCodeEnum.GENERAL_USER.getCode())), user.getId());
            
            // 创建社交账号绑定关系
            userSocial = new UserSocialDO();
            userSocial.setUserId(user.getId());
            userSocial.setSource(source);
            userSocial.setOpenId(openId);
            
            // 发送安全消息
            this.sendSecurityMsg(user);
        } else {
            // 已绑定用户，获取用户信息
            user = BeanUtil.copyProperties(userService.getById(userSocial.getUserId()), UserDO.class);
        }
        
        // 检查用户状态
        super.checkUserStatus(user);
        
        // 更新社交账号信息
        userSocial.setMetaJson(JSONUtil.toJsonStr(wechatUserInfo));
        userSocial.setLastLoginTime(LocalDateTime.now());
        userSocialService.saveOrUpdate(userSocial);
        
        // 执行认证
        return super.authenticate(user, client);
    }

    @Override
    public void preLogin(WechatMiniProgramLoginReq req, ClientResp client, HttpServletRequest request) {
        super.preLogin(req, client, request);
        if (StpUtil.isLogin()) {
            StpUtil.logout();
        }
    }

    @Override
    public AuthTypeEnum getAuthType() {
        return AuthTypeEnum.WECHAT_MINI_PROGRAM;
    }

    /**
     * 注册新用户
     *
     * @param wechatUserInfo 微信用户信息
     * @return 用户信息
     */
    private UserDO registerNewUser(WechatUserInfo wechatUserInfo) {

        String randomStr = RandomUtil.randomString(RandomUtil.BASE_CHAR_NUMBER_LOWER, 5);
        String username = this.generateUsername(randomStr);
        String validNickname = this.generateValidNickname(randomStr);

        UserDO user = new UserDO();
        user.setUsername(username);
        user.setNickname(validNickname);
        user.setGender(GenderEnum.UNKNOWN);
        user.setAvatar("https://ts4.tc.mm.bing.net/th/id/OIP-C.m2pHSh1rSVjYe9KDvIPDkQAAAA?rs=1&pid=ImgDetMain&o=7&rm=3");
        user.setDeptId(SystemConstants.SUPER_DEPT_ID);
        user.setStatus(DisEnableStatusEnum.ENABLE);

        userService.save(user);
        return user;
    }

    /**
     * 生成有效的用户名
     *
     * @param nickname 昵称
     * @return 用户名
     */
    private String generateUsername(String nickname) {
        String username = nickname;
        String randomStr = RandomUtil.randomString(RandomUtil.BASE_CHAR, 5);
        
        // 检查昵称是否符合用户名规范
        if (StrUtil.isBlank(username) || !ReUtil.isMatch(RegexConstants.USERNAME, username)) {
            username = "wechat" + randomStr + IdUtil.fastSimpleUUID().substring(0, 8);
        } else {
            // 检查用户名是否已存在
            UserDO existsUser = userService.getByUsername(username);
            if (existsUser != null) {
                username = username + randomStr + IdUtil.fastSimpleUUID().substring(0, 8);
            }
        }
        
        return username;
    }

    /**
     * 生成有效的昵称
     *
     * @param nickname 原昵称
     * @return 有效昵称
     */
    private String generateValidNickname(String nickname) {
        if (StrUtil.isBlank(nickname) || !ReUtil.isMatch(RegexConstants.GENERAL_NAME, nickname)) {
            String randomStr = RandomUtil.randomString(RandomUtil.BASE_CHAR, 5);
            return "wx_" + randomStr;
        }
        return nickname;
    }

    /**
     * 发送安全消息
     *
     * @param user 用户信息
     */
    private void sendSecurityMsg(UserDO user) {
        MessageTemplateEnum template = MessageTemplateEnum.SOCIAL_REGISTER;
        MessageReq req = new MessageReq(MessageTypeEnum.SECURITY);
        req.setTitle(template.getTitle().formatted(applicationProperties.getName()));
        req.setContent(template.getContent().formatted(user.getNickname()));
        req.setPath(template.getPath());
        messageService.add(req, CollUtil.toList(user.getId().toString()));
    }
}